/*
 *
 *  Connection Manager
 *
 *  Copyright (C) 2013  Intel Corporation. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>

#include <gdbus.h>

#include "dbus_helpers.h"
#include "agent.h"
#include "log.h"

#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "agent.c"

#define AGENT_INTERFACE      "net.connman.Agent"
#define VPN_AGENT_INTERFACE  "net.connman.vpn.Agent"

static DBusConnection *agent_connection;

struct agent_input_data {
    const char *attribute;
    bool requested;
    char *prompt;
    void (*func)(char *input, void *user_data);
    char *input;
};

struct agent_data {
    struct agent_input_data *input;
    char *interface;
    bool registered;
    DBusMessage *message;
    DBusMessage *reply;
    DBusMessageIter iter;
    DBusMessageIter dict;
    GDBusMethodFunction pending_function;
};

static void request_input_ssid_return(char *input, void *user_data);
static void request_input_passphrase_return(char *input, void *user_data);
static void request_input_string_return(char *input, void *user_data);

enum requestinput {
    SSID                    = 0,
    IDENTITY                = 1,
    PASSPHRASE              = 2,
    WPS                     = 3,
    WISPR_USERNAME          = 4,
    WISPR_PASSPHRASE        = 5,
    REQUEST_INPUT_MAX       = 6,
};

static struct agent_input_data agent_input_handler[] = {
    { "Name", false, "Hidden SSID name? ", request_input_ssid_return, NULL },
    { "Identity", false, "EAP username? ", request_input_string_return, NULL },
    {
        "Passphrase", false, "Passphrase? ",
        request_input_passphrase_return, NULL
    },
    {
        "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
        request_input_string_return, NULL
    },
    { "Username", false, "WISPr username? ", request_input_string_return, NULL },
    { "Password", false, "WISPr password? ", request_input_string_return, NULL },
    { },
};

static struct agent_data agent_request = {
    agent_input_handler,
    AGENT_INTERFACE,
};

static struct agent_input_data vpnagent_input_handler[] = {
    {
        "OpenConnect.Cookie", false, "OpenConnect Cookie? ",
        request_input_string_return
    },
    {
        "OpenConnect.ServerCert", false,
        "OpenConnect server certificate hash? ",
        request_input_string_return
    },
    {
        "OpenConnect.VPNHost", false, "OpenConnect VPN server? ",
        request_input_string_return
    },
    { "Username", false, "VPN username? ", request_input_string_return },
    { "Password", false, "VPN password? ", request_input_string_return },
    { },
};

void set_passphrase(char *input)
{
    int i;
    LOG_INFO("%s,%s\n", __func__, input);

    if (agent_input_handler[PASSPHRASE].input)
        g_free(agent_input_handler[PASSPHRASE].input);
    agent_input_handler[PASSPHRASE].input = g_strdup(input);
}

static struct agent_data vpn_agent_request = {
    vpnagent_input_handler,
    VPN_AGENT_INTERFACE,
};

static int confirm_input(char *input)
{
    int i;

    if (!input)
        return -1;

    for (i = 0; input[i] != '\0'; i++)
        if (isspace(input[i]) == 0)
            break;

    if (strcasecmp(&input[i], "yes") == 0 ||
        strcasecmp(&input[i], "y") == 0)
        return 1;

    if (strcasecmp(&input[i], "no") == 0 ||
        strcasecmp(&input[i], "n") == 0)
        return 0;

    return -1;
}

static char *strip_path(char *path)
{
    char *name = strrchr(path, '/');
    if (name)
        name++;
    else
        name = path;

    return name;
}

static char *agent_path(void)
{
    static char *path = NULL;

    if (!path)
        path = g_strdup_printf("/net/connman/connmanctl%d", getpid());

    return path;
}

static void pending_message_remove(struct agent_data *request)
{
    if (request->message) {
        dbus_message_unref(request->message);
        request->message = NULL;
    }

    if (request->reply) {
        dbus_message_unref(request->reply);
        request->reply = NULL;
    }
}

static void pending_command_complete(char *message)
{
    struct agent_data *next_request = NULL;
    DBusMessage *pending_message;
    GDBusMethodFunction pending_function;
    LOG_INFO("%s\n", __func__);
    //__connmanctl_save_rl();

    LOG_INFO("%s", message);

    //__connmanctl_redraw_rl();

    //if (__connmanctl_is_interactive() == true)
    //  __connmanctl_command_mode();
    //else
    //  __connmanctl_agent_mode("", NULL, NULL);

    if (agent_request.message)
        next_request = &agent_request;
    else if (vpn_agent_request.message)
        next_request = &vpn_agent_request;

    if (!next_request)
        return;

    pending_message = next_request->message;
    pending_function = next_request->pending_function;
    next_request->pending_function = NULL;

    pending_function(agent_connection, next_request->message,
                     next_request);

    dbus_message_unref(pending_message);
}

static bool handle_message(DBusMessage *message, struct agent_data *request,
                           GDBusMethodFunction function)
{
    if (!agent_request.pending_function &&
        !vpn_agent_request.pending_function)
        return true;

    request->message = dbus_message_ref(message);
    request->pending_function = function;

    return false;
}

static DBusMessage *agent_release(DBusConnection *connection,
                                  DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request, agent_release) == false)
        return NULL;

    g_dbus_unregister_interface(connection, agent_path(),
                                request->interface);
    request->registered = false;

    pending_message_remove(request);

    if (strcmp(request->interface, AGENT_INTERFACE) == 0)
        pending_command_complete("Agent unregistered by ConnMan\n");
    else
        pending_command_complete("VPN Agent unregistered by ConnMan "
                                 "VPNd\n");

    //if (__connmanctl_is_interactive() == false)
    //  __connmanctl_quit();

    return dbus_message_new_method_return(message);
}

static DBusMessage *agent_cancel(DBusConnection *connection,
                                 DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;

    if (handle_message(message, request, agent_cancel) == false)
        return NULL;

    pending_message_remove(request);

    if (strcmp(request->interface, AGENT_INTERFACE) == 0)
        pending_command_complete("Agent request cancelled by "
                                 "ConnMan\n");
    else
        pending_command_complete("VPN Agent request cancelled by "
                                 "ConnMan VPNd\n");

    return dbus_message_new_method_return(message);
}

static void request_browser_return(char *input, void *user_data)
{
    struct agent_data *request = user_data;

    switch (confirm_input(input)) {
    case 1:
        g_dbus_send_reply(agent_connection, request->message,
                          DBUS_TYPE_INVALID);
        break;
    case 0:
        g_dbus_send_error(agent_connection, request->message,
                          "net.connman.Agent.Error.Canceled", NULL);
        break;
    default:
        return;
    }

    pending_message_remove(request);
    pending_command_complete("");
}

static DBusMessage *agent_request_browser(DBusConnection *connection,
                                          DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    DBusMessageIter iter;
    char *service, *url;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request, agent_request_browser) == false)
        return NULL;

    dbus_message_iter_init(message, &iter);

    dbus_message_iter_get_basic(&iter, &service);
    dbus_message_iter_next(&iter);
    dbus_message_iter_get_basic(&iter, &url);

    //__connmanctl_save_rl();
    LOG_INFO("Agent RequestBrowser %s\n", strip_path(service));
    LOG_INFO("  %s\n", url);
    //__connmanctl_redraw_rl();

    request->message = dbus_message_ref(message);
    //__connmanctl_agent_mode("Connected (yes/no)? ",
    //      request_browser_return, request);
    request_browser_return("yes", request);

    return NULL;
}

static void report_error_return(char *input, void *user_data)
{
    struct agent_data *request = user_data;

    switch (confirm_input(input)) {
    case 1:
        if (strcmp(request->interface, AGENT_INTERFACE) == 0)
            g_dbus_send_error(agent_connection, request->message,
                              "net.connman.Agent.Error.Retry", NULL);
        else
            g_dbus_send_error(agent_connection, request->message,
                              "net.connman.vpn.Agent.Error.Retry",
                              NULL);
        break;
    case 0:
        g_dbus_send_reply(agent_connection, request->message,
                          DBUS_TYPE_INVALID);
        break;
    default:
        return;
    }

    pending_message_remove(request);
    pending_command_complete("");
}

static DBusMessage *agent_report_error(DBusConnection *connection,
                                       DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    DBusMessageIter iter;
    char *path, *service, *error;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request, agent_report_error) == false)
        return NULL;

    dbus_message_iter_init(message, &iter);

    dbus_message_iter_get_basic(&iter, &path);
    service = strip_path(path);

    dbus_message_iter_next(&iter);
    dbus_message_iter_get_basic(&iter, &error);

    //__connmanctl_save_rl();
    if (strcmp(request->interface, AGENT_INTERFACE) == 0)
        LOG_INFO("Agent ReportError %s\n", service);
    else
        LOG_INFO("VPN Agent ReportError %s\n", service);
    LOG_INFO("  %s\n", error);
    //__connmanctl_redraw_rl();

    request->message = dbus_message_ref(message);
    //__connmanctl_agent_mode("Retry (yes/no)? ", report_error_return,
    //      request);
    report_error_return("no", request);

    return NULL;
}

static DBusMessage *agent_report_peer_error(DBusConnection *connection,
                                            DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    char *path, *peer, *error;
    DBusMessageIter iter;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request,
                       agent_report_peer_error) == false)
        return NULL;

    dbus_message_iter_init(message, &iter);

    dbus_message_iter_get_basic(&iter, &path);
    peer = strip_path(path);

    dbus_message_iter_next(&iter);
    dbus_message_iter_get_basic(&iter, &error);

    //__connmanctl_save_rl();
    LOG_INFO("Agent ReportPeerError %s\n", peer);
    LOG_INFO("  %s\n", error);
    //__connmanctl_redraw_rl();

    request->message = dbus_message_ref(message);
    //__connmanctl_agent_mode("Retry (yes/no)? ",
    //          report_error_return, request);
    report_error_return("no", request);
    return NULL;
}

static void request_input_next(struct agent_data *request)
{
    int i;
    LOG_INFO("%s\n", __func__);

    for (i = 0; request->input[i].attribute; i++) {
        if (request->input[i].requested == true) {
            if (request->input[i].func) {
                //__connmanctl_agent_mode(request->input[i].prompt,
                //      request->input[i].func,
                //      request);
                if (request->input[i].input) {
                    request->input[i].func(request->input[i].input, request);
                    g_free(request->input[i].input);
                    request->input[i].input = 0;
                } else {
                    request->input[i].func("", request);
                }
            } else
                request->input[i].requested = false;
            return;
        }
    }

    dbus_message_iter_close_container(&request->iter, &request->dict);

    g_dbus_send_message(agent_connection, request->reply);
    request->reply = NULL;

    pending_message_remove(request);
    pending_command_complete("");

    //__connmanctl_redraw_rl();
}

static void request_input_append(struct agent_data *request,
                                 const char *attribute, char *value)
{
    __connmanctl_dbus_append_dict_entry(&request->dict, attribute,
                                        DBUS_TYPE_STRING, &value);
}

static void request_input_ssid_return(char *input,
                                      void *user_data)
{
    struct agent_data *request = user_data;
    int len = 0;
    LOG_INFO("%s\n", __func__);
    if (input)
        len = strlen(input);

    if (len > 0 && len <= 32) {
        request->input[SSID].requested = false;
        request_input_append(request, request->input[SSID].attribute,
                             input);

        request_input_next(request);
    }
}

static void request_input_passphrase_return(char *input, void *user_data)
{
    struct agent_data *request = user_data;
    int len = 0;

    /* TBD passphrase length checking */
    LOG_INFO("%s input = %s\n", __func__, input);
    if (input)
        len = strlen(input);

    if (len == 0 && request->input[WPS].requested == false)
        return;

    request->input[PASSPHRASE].requested = false;

    if (len > 0) {
        request_input_append(request,
                             request->input[PASSPHRASE].attribute, input);

        request->input[WPS].requested = false;
    }

    request_input_next(request);
}

static void request_input_string_return(char *input, void *user_data)
{
    struct agent_data *request = user_data;
    int i;
    LOG_INFO("%s\n", __func__);
    for (i = 0; request->input[i].attribute; i++) {
        if (request->input[i].requested == true) {
            request_input_append(request,
                                 request->input[i].attribute, input);
            request->input[i].requested = false;
            break;
        }
    }

    request_input_next(request);
}

static void parse_agent_request(struct agent_data *request,
                                DBusMessageIter *iter)
{
    DBusMessageIter dict, entry, variant, dict_entry;
    DBusMessageIter field_entry, field_value;
    char *field, *argument, *value;
    char *attr_type = NULL;
    int i;
    LOG_INFO("%s\n", __func__);
    dbus_message_iter_recurse(iter, &dict);

    while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {

        dbus_message_iter_recurse(&dict, &entry);

        dbus_message_iter_get_basic(&entry, &field);
        LOG_INFO("%s field = %s\n", __func__, field);

        dbus_message_iter_next(&entry);

        dbus_message_iter_recurse(&entry, &variant);
        dbus_message_iter_recurse(&variant, &dict_entry);

        while (dbus_message_iter_get_arg_type(&dict_entry)
               == DBUS_TYPE_DICT_ENTRY) {
            dbus_message_iter_recurse(&dict_entry, &field_entry);

            dbus_message_iter_get_basic(&field_entry, &argument);

            dbus_message_iter_next(&field_entry);

            dbus_message_iter_recurse(&field_entry, &field_value);

            if (strcmp(argument, "Type") == 0) {
                dbus_message_iter_get_basic(&field_value,
                                            &value);
                attr_type = g_strdup(value);
            }

            dbus_message_iter_next(&dict_entry);
        }

        for (i = 0; request->input[i].attribute; i++) {
            if (strcmp(field, request->input[i].attribute) == 0) {
                request->input[i].requested = true;
                break;
            }
        }

        g_free(attr_type);
        attr_type = NULL;

        dbus_message_iter_next(&dict);
    }
}

static DBusMessage *agent_request_input(DBusConnection *connection,
                                        DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    DBusMessageIter iter, dict;
    char *service, *str;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request, agent_request_input) == false)
        return NULL;

    dbus_message_iter_init(message, &iter);

    dbus_message_iter_get_basic(&iter, &str);
    service = strip_path(str);

    dbus_message_iter_next(&iter);
    dbus_message_iter_recurse(&iter, &dict);

    //__connmanctl_save_rl();
    if (strcmp(request->interface, AGENT_INTERFACE) == 0)
        LOG_INFO("Agent RequestInput %s\n", service);
    else
        LOG_INFO("VPN Agent RequestInput %s\n", service);
    __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
    LOG_INFO("\n");

    parse_agent_request(request, &iter);

    request->reply = dbus_message_new_method_return(message);
    dbus_message_iter_init_append(request->reply, &request->iter);

    dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
                                     DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
                                     DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                     &request->dict);

    request_input_next(request);

    return NULL;
}

static void request_authorization_return(char *input, void *user_data)
{
    struct agent_data *request = user_data;

    switch (confirm_input(input)) {
    case 1:
        request->reply = dbus_message_new_method_return(
                             request->message);
        dbus_message_iter_init_append(request->reply, &request->iter);

        dbus_message_iter_open_container(&request->iter,
                                         DBUS_TYPE_ARRAY,
                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                         DBUS_TYPE_STRING_AS_STRING
                                         DBUS_TYPE_VARIANT_AS_STRING
                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                         &request->dict);
        dbus_message_iter_close_container(&request->iter,
                                          &request->dict);
        g_dbus_send_message(agent_connection, request->reply);
        request->reply = NULL;
        break;
    case 0:
        g_dbus_send_error(agent_connection, request->message,
                          "net.connman.Agent.Error.Rejected", NULL);
        break;
    default:
        g_dbus_send_error(agent_connection, request->message,
                          "net.connman.Agent.Error.Canceled", NULL);
        break;
    }

    pending_message_remove(request);
    pending_command_complete("");
}

static DBusMessage *
agent_request_peer_authorization(DBusConnection *connection,
                                 DBusMessage *message, void *user_data)
{
    struct agent_data *request = user_data;
    DBusMessageIter iter, dict;
    char *peer, *str;
    bool input;
    int i;
    LOG_INFO("%s\n", __func__);
    if (handle_message(message, request, agent_request_peer_authorization)
        == false)
        return NULL;

    dbus_message_iter_init(message, &iter);

    dbus_message_iter_get_basic(&iter, &str);
    peer = strip_path(str);

    dbus_message_iter_next(&iter);
    dbus_message_iter_recurse(&iter, &dict);

    //__connmanctl_save_rl();
    LOG_INFO("Agent RequestPeerAuthorization %s\n", peer);
    __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
    LOG_INFO("\n");

    parse_agent_request(request, &iter);

    for (input = false, i = 0; request->input[i].attribute; i++) {
        if (request->input[i].requested == true) {
            input = true;
            break;
        }
    }

    if (!input) {
        request->message = dbus_message_ref(message);
        //__connmanctl_agent_mode("Accept connection (yes/no)? ",
        //          request_authorization_return, request);
        request_authorization_return("yes", request);
        return NULL;
    }

    request->reply = dbus_message_new_method_return(message);
    dbus_message_iter_init_append(request->reply, &request->iter);

    dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
                                     DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
                                     DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                     &request->dict);

    request_input_next(request);

    return NULL;
}

static const GDBusMethodTable agent_methods[] = {
    { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
    { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
    {
        GDBUS_ASYNC_METHOD("RequestBrowser",
        GDBUS_ARGS({ "service", "o" },
        { "url", "s" }),
        NULL, agent_request_browser)
    },
    {
        GDBUS_ASYNC_METHOD("ReportError",
        GDBUS_ARGS({ "service", "o" },
        { "error", "s" }),
        NULL, agent_report_error)
    },
    {
        GDBUS_ASYNC_METHOD("ReportPeerError",
        GDBUS_ARGS({ "peer", "o" },
        { "error", "s" }),
        NULL, agent_report_peer_error)
    },
    {
        GDBUS_ASYNC_METHOD("RequestInput",
        GDBUS_ARGS({ "service", "o" },
        { "fields", "a{sv}" }),
        GDBUS_ARGS({ "fields", "a{sv}" }),
        agent_request_input)
    },
    {
        GDBUS_ASYNC_METHOD("RequestPeerAuthorization",
        GDBUS_ARGS({ "peer", "o" },
        { "fields", "a{sv}" }),
        GDBUS_ARGS({ "fields", "a{sv}" }),
        agent_request_peer_authorization)
    },
    { },
};

static int agent_register_return(DBusMessageIter *iter, const char *error,
                                 void *user_data)
{
    DBusConnection *connection = user_data;

    if (error) {
        g_dbus_unregister_interface(connection, agent_path(),
                                    AGENT_INTERFACE);
        LOG_ERROR("Error registering Agent: %s\n", error);
        return 0;
    }

    agent_request.registered = true;
    LOG_INFO("Agent registered\n");

    return -EINPROGRESS;
}

static void append_path(DBusMessageIter *iter, void *user_data)
{
    const char *path = user_data;

    dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
}

int __connmanctl_agent_register(DBusConnection *connection)
{
    char *path = agent_path();
    int result;

    if (agent_request.registered == true) {
        LOG_ERROR("Agent already registered\n");
        return -EALREADY;
    }

    agent_connection = connection;

    if (!g_dbus_register_interface(connection, path,
                                   AGENT_INTERFACE, agent_methods,
                                   NULL, NULL, &agent_request, NULL)) {
        LOG_ERROR("Error: Failed to register Agent callbacks\n");
        return 0;
    }

    result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                                           CONNMAN_PATH, "net.connman.Manager", "RegisterAgent",
                                           agent_register_return, connection, append_path, path);

    if (result != -EINPROGRESS) {
        g_dbus_unregister_interface(connection, agent_path(),
                                    AGENT_INTERFACE);

        LOG_ERROR("Error: Failed to register Agent\n");
    }

    return result;
}

static int agent_unregister_return(DBusMessageIter *iter, const char *error,
                                   void *user_data)
{
    if (error) {
        LOG_ERROR("Error unregistering Agent: %s\n", error);
        return 0;
    }

    agent_request.registered = false;
    LOG_INFO("Agent unregistered\n");

    return 0;
}

int __connmanctl_agent_unregister(DBusConnection *connection)
{
    char *path = agent_path();
    int result;

    if (agent_request.registered == false) {
        LOG_ERROR("Agent not registered\n");
        return -EALREADY;
    }

    g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);

    result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                                           CONNMAN_PATH, "net.connman.Manager", "UnregisterAgent",
                                           agent_unregister_return, NULL, append_path, path);

    if (result != -EINPROGRESS)
        LOG_ERROR("Error: Failed to unregister Agent\n");

    return result;
}

static const GDBusMethodTable vpn_agent_methods[] = {
    { GDBUS_ASYNC_METHOD("Release", NULL, NULL, agent_release) },
    { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, agent_cancel) },
    {
        GDBUS_ASYNC_METHOD("ReportError",
        GDBUS_ARGS({ "service", "o" },
        { "error", "s" }),
        NULL, agent_report_error)
    },
    {
        GDBUS_ASYNC_METHOD("RequestInput",
        GDBUS_ARGS({ "service", "o" },
        { "fields", "a{sv}" }),
        GDBUS_ARGS({ "fields", "a{sv}" }),
        agent_request_input)
    },
    { },
};

static int vpn_agent_register_return(DBusMessageIter *iter, const char *error,
                                     void *user_data)
{
    DBusConnection *connection = user_data;

    if (error) {
        g_dbus_unregister_interface(connection, agent_path(),
                                    VPN_AGENT_INTERFACE);
        LOG_ERROR("Error registering VPN Agent: %s\n", error);
        return 0;
    }

    vpn_agent_request.registered = true;
    LOG_INFO("VPN Agent registered\n");

    return -EINPROGRESS;
}

int __connmanctl_vpn_agent_register(DBusConnection *connection)
{
    char *path = agent_path();
    int result;

    if (vpn_agent_request.registered == true) {
        LOG_ERROR("VPN Agent already registered\n");
        return -EALREADY;
    }

    agent_connection = connection;

    if (!g_dbus_register_interface(connection, path,
                                   VPN_AGENT_INTERFACE, vpn_agent_methods,
                                   NULL, NULL, &vpn_agent_request, NULL)) {
        LOG_ERROR("Error: Failed to register VPN Agent "
                "callbacks\n");
        return 0;
    }

    result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
                                           VPN_PATH, "net.connman.vpn.Manager", "RegisterAgent",
                                           vpn_agent_register_return, connection, append_path,
                                           path);

    if (result != -EINPROGRESS) {
        g_dbus_unregister_interface(connection, agent_path(),
                                    VPN_AGENT_INTERFACE);

        LOG_ERROR("Error: Failed to register VPN Agent\n");
    }

    return result;
}

static int vpn_agent_unregister_return(DBusMessageIter *iter,
                                       const char *error, void *user_data)
{
    if (error) {
        LOG_ERROR("Error unregistering VPN Agent: %s\n", error);
        return 0;
    }

    vpn_agent_request.registered = false;
    LOG_INFO("VPN Agent unregistered\n");

    return 0;
}

int __connmanctl_vpn_agent_unregister(DBusConnection *connection)
{
    char *path = agent_path();
    int result;

    if (vpn_agent_request.registered == false) {
        LOG_ERROR("VPN Agent not registered\n");
        return -EALREADY;
    }

    g_dbus_unregister_interface(connection, agent_path(),
                                VPN_AGENT_INTERFACE);

    result = __connmanctl_dbus_method_call(connection, VPN_SERVICE,
                                           VPN_PATH, "net.connman.vpn.Manager", "UnregisterAgent",
                                           vpn_agent_unregister_return, NULL, append_path, path);

    if (result != -EINPROGRESS)
        LOG_ERROR("Error: Failed to unregister VPN Agent\n");

    return result;
}
